/* prod_no_condvar.c

   A simple POSIX threads producer-consumer example that doesn't use
   a condition variable.

   See also prod_condvar.c.
*/
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

void errExitEN(int errn, char *msg) {
  fprintf(stderr, "%s: %s\n", msg, strerror(errn));
  exit(EXIT_FAILURE);
}

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static int avail = 0;

static void *producer(void *arg) {
  int cnt = atoi((char *)arg);
  int s, j;

  for (j = 0; j < cnt; j++) {
    sleep(1);

    /* Code to produce a unit omitted */
    s = pthread_mutex_lock(&mtx);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_lock");
    }

    avail++; /* Let consumer know another unit is available */

    s = pthread_mutex_unlock(&mtx);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_unlock");
    }
  }

  return NULL;
}

int main(int argc, char *argv[]) {
  pthread_t tid;
  int s, j;
  int totRequired; /* Total number of units that all threads will produce */
  int numConsumed; /* Total units so far consumed */

  bool done;
  time_t t;

  t = time(NULL);

  /* Create all threads */
  totRequired = 0;
  for (j = 1; j < argc; j++) {
    totRequired += atoi(argv[j]);

    s = pthread_create(&tid, NULL, producer, argv[j]);
    if (s != 0) {
      errExitEN(s, "pthread_create");
    }
  }

  /* Use a polling loop to check for available units */
  numConsumed = 0;
  done = false;

  for (;;) {
    s = pthread_mutex_lock(&mtx);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_lock");
    }

    while (avail > 0) { /* Consume all available units */
      /* Do something with produced unit */
      numConsumed++;
      avail--;
      printf("T=%ld: numConsumed=%d\n", (long)(time(NULL) - t), numConsumed);

      done = numConsumed >= totRequired;
    }

    s = pthread_mutex_unlock(&mtx);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_unlock");
    }

    if (done) {
      break;
    }
    /* Perhaps do other work here that does not require mutex lock */
  }

  exit(EXIT_SUCCESS);
}